//
//  BIBluetoothModule.m
//  BIPushModule
//
//  Created by yown on 2022/3/5.
//

#import "BIBluetoothManager.h"
#import <BIDebugModule/BIDebugManager.h>

#import <BIToolModule/BIUserDefault.h>

#import <BIToolModule/NSData+Helper.h>
#import <BIToolModule/NSMutableArray+Helper.h>

#import <AVFoundation/AVFoundation.h>

/// 历史连接过的蓝牙设备的UUID
static NSString *const kSaveBluetoothUUIDList = @"kSaveBluetoothUUIDList";
/// DEBUG的log类型
static NSString *const kBluetoothLogType = @"Bluetooth";
/// 连接蓝牙的最长时间
static const NSUInteger connectMaxTime = 5;

@interface BIBluetoothManager () <CBCentralManagerDelegate, CBPeripheralDelegate>

/// 蓝牙控制器
@property (nonatomic) CBCentralManager *centeralManager;
/// 扫描到的所有蓝牙设备列表<BIBluetoothModel>
@property (nonatomic, readwrite) NSMutableArray <BIBluetoothModel *>*allPeripherals;
/// 历史连接过的蓝牙设备列表<BIBluetoothModel>
@property (nonatomic, readwrite) NSMutableArray <BIBluetoothModel *>*connectedPeripherals;
/// 蓝牙设备扫描队列
@property (nonatomic) dispatch_queue_t scanQueue;
/// 是否正在扫描
@property (nonatomic, assign) BOOL isScan;
/// 读特征的UUID字典
@property (nonatomic, copy) NSDictionary *readUUIDDictionary;
/// 写特征的UUID字典
@property (nonatomic, copy) NSDictionary *writeUUIDDictionary;

/// 连接的计时器
@property (nonatomic) NSTimer *timer;

@end

@implementation BIBluetoothManager

#pragma mark - Public Method

BISingletonImplementation()

- (instancetype)init {
    self = [super init];
    if (self) {
        self.scanQueue = dispatch_queue_create("BIBluetoothScanQueue", DISPATCH_QUEUE_CONCURRENT);
        self.centeralManager = [[CBCentralManager alloc] initWithDelegate:self queue:self.scanQueue];
        self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(yy_repeatTimer) userInfo:nil repeats:YES];
        [[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];

        self.allPeripherals = [NSMutableArray array];
        self.connectedPeripherals = [NSMutableArray array];
        self.isScan = NO;
    }
    return self;
}

- (CBManagerState)state {
    return self.centeralManager.state;
}

- (BIBluetoothModel *)currentBluetoothModel {
    __block BIBluetoothModel *tempModel = nil;
    [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.state == kBIBluetoothConnected && obj.selectTimeInterval > tempModel.selectTimeInterval) {
            tempModel = obj;
        }
    }];
    return tempModel;
}

- (void)launchWithWriteUUIDS:(NSDictionary *)writeUUIDS readUUIDS:(NSDictionary *)readUUIDS {
    self.writeUUIDDictionary = writeUUIDS;
    self.readUUIDDictionary = readUUIDS;
}

- (void)scanBluetoothList {
    if (!self.isScan) {
        NSMutableArray *deleteArray = [NSMutableArray array];
        [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if (obj.state != kBIBluetoothConnected) {
                [deleteArray addObject:obj];
            }
        }];
        [self.allPeripherals removeObjectsInArray:deleteArray];
        [self.centeralManager scanForPeripheralsWithServices:nil options:nil];
    }
    self.isScan = YES;
}

- (void)stopScan {
    if (self.isScan) {
        [self.centeralManager stopScan];
    }
    self.isScan = NO;
}

- (void)connectBluetooth:(BIBluetoothModel *)model {
    if (model.peripheral && model.state == kBIBluetoothDisconnected) {
        NSString *log = [NSString stringWithFormat:@"连接设备---蓝牙名称=%@;\n蓝牙mac地址=%@;\n", model.name, model.macAddress];
        BITypeLog(kBluetoothLogType, log);
        model.state = kBIBluetoothConnecting;
        [self yy_postNotification:kBluetoothConnectResultNotification object:model];
        [self.centeralManager connectPeripheral:model.peripheral options:nil];
    }
    
//    if (model.peripheral && model.state == kBIBluetoothDisconnected) {
//        NSString *outputUUID = [self yy_getConnectedBluetoothUUID];
//        NSString *log = [NSString stringWithFormat:@"连接设备---蓝牙名称=%@;\n蓝牙mac地址=%@;\n外设地址=%@;\n", model.name, model.macAddress, outputUUID];
//        BITypeLog(kBluetoothLogType, log);
//        model.state = kBIBluetoothConnecting;
//        // 判断硬件mac地址是否与当前连接的外设mac地址一致
//        if ([model.macAddress isEqualToString:outputUUID]) {
//            model.state = kBIBluetoothConnecting;
//            [self yy_postNotification:kBluetoothConnectResultNotification object:model];
//            [self.centeralManager connectPeripheral:model.peripheral options:nil];
//        } else {
//            [model disconnectBluetooth];
//            [self yy_postNotification:kBluetoothConnectResultNotification object:model];
//        }
//    }
}

- (void)disconnectBluetooth:(BIBluetoothModel *)model {
    if (!model) {
        [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
            if (obj.state == kBIBluetoothConnected) {
                [self.centeralManager cancelPeripheralConnection:obj.peripheral];
            }
        }];
    } else {
        if (model.state == kBIBluetoothConnected) {
            [self.centeralManager cancelPeripheralConnection:model.peripheral];
        }
    }
}

- (void)sendData:(NSData *)data model:(BIBluetoothModel *)model {
    if (model.peripheral && model.state == kBIBluetoothConnected && model.writeCharacteristic) {
        [self yy_sendData:@[model] data:data];
    } else {
        [self yy_sendData:self.allPeripherals data:data];
    }
}

#pragma mark - Private Method

//// 获取A2DP或者HFP类型已连接的外设UUID
//- (NSString *)yy_getConnectedBluetoothUUID {
//    __block NSString *uuid = nil;
//    AVAudioSession *audioSession = [AVAudioSession sharedInstance];
//    AVAudioSessionRouteDescription *route = audioSession.currentRoute;
//    [route.outputs enumerateObjectsUsingBlock:^(AVAudioSessionPortDescription * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
//        if ([obj.portType isEqualToString:@"BluetoothA2DPOutput"] ||
//            [obj.portType isEqualToString:@"BluetoothHFPOutput"]) {
//            uuid = obj.UID;
//            *stop = YES;
//        }
//    }];
//    
//    if (uuid.length > 17) {
//        return [uuid substringToIndex:17];
//    } else {
//        return uuid;
//    }
//}

- (void)yy_postNotification:(NSString *)name object:(id)object {
    dispatch_block_t block = ^{
        [[NSNotificationCenter defaultCenter] postNotificationName:name object:object];
    };
    BISafeBlockOnMainQueue(block);
}

- (NSInteger)yy_checkDeviceModelHasList:(NSArray <BIBluetoothModel *>*)list model:(BIBluetoothModel *)model {
    __block NSInteger repeatIndex = -1;
    [list enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        // 如果这个蓝牙设备的名字与uuid与数组中的相同，则说明设备蓝牙过，则删除旧的，存入新的
        if ([obj.name isEqualToString:model.name] && [obj.uuid isEqualToString:model.uuid]) {
            repeatIndex = idx;
            *stop = YES;
        }
    }];
    return repeatIndex;
}

- (void)yy_closeBluetooth {
    [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.state != kBIBluetoothDisconnected) {
            obj.state = kBIBluetoothDisconnected;
            NSString *log = [NSString stringWithFormat:@"断开设备---蓝牙名称=%@;\n蓝牙mac地址=%@;\n", obj.name, obj.macAddress];
            BITypeLog(kBluetoothLogType, log);
            [obj disconnectBluetooth];
            [self yy_postNotification:kBluetoothConnectResultNotification object:obj];
        }
    }];
    
    [self.allPeripherals removeAllObjects];
    [self.connectedPeripherals removeAllObjects];
}

- (void)yy_repeatTimer {
    [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.state == kBIBluetoothConnecting) {
            obj.connectTimeInterval += 1;
            if (obj.connectTimeInterval >= connectMaxTime) {
                obj.state == kBIBluetoothDisconnected;
                NSString *log = [NSString stringWithFormat:@"连接设备超时---蓝牙名称=%@;\n蓝牙mac地址=%@;\n", obj.name, obj.macAddress];
                BITypeLog(kBluetoothLogType, log);
                [obj disconnectBluetooth];
                [self yy_postNotification:kBluetoothConnectResultNotification object:obj];
            }
        } else {
            obj.connectTimeInterval = 0;
        }
    }];
}

- (void)yy_sendData:(NSArray *)modelArray data:(NSData *)data {
    [modelArray enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.peripheral && obj.state == kBIBluetoothConnected && obj.writeCharacteristic) {
            NSString *log = [NSString stringWithFormat:@"发送数据---蓝牙名称=%@;\n蓝牙数据=%@;\n", obj.name, data];
            BITypeLog(kBluetoothLogType, log);
            CBCharacteristicWriteType type = CBCharacteristicWriteWithResponse;
            if (obj.writeCharacteristic.properties == CBCharacteristicPropertyWriteWithoutResponse) {
                type = CBCharacteristicWriteWithoutResponse;
            }
            [obj.peripheral writeValue:data forCharacteristic:obj.writeCharacteristic type:type];
        }
    }];
}

#pragma mark - CBCentralManagerDelegate

/// 1、蓝牙权限变化
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    // 当外部蓝牙状态发生变化时，修改isScan状态与外部状态一直
    // 蓝牙扫描需要再这个函数里面调用，更早的声明周期是无效的
    if (central.state == CBManagerStatePoweredOn) {
        BITypeLog(kBluetoothLogType, @"开启蓝牙;\n");
        self.isScan = NO;
        [self scanBluetoothList];
    } else {
        BITypeLog(kBluetoothLogType, @"关闭蓝牙;\n");
        self.isScan = YES;
        [self stopScan];
        // 关闭蓝牙开关后，需要手动去处理蓝牙的连接状态以及本地存储的蓝牙数据
        [self yy_closeBluetooth];
    }
    [self yy_postNotification:kBluetoothStateNotification object:@(central.state)];
}

/// 2、扫描到的蓝牙设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
    
    NSString *name = advertisementData[CBAdvertisementDataLocalNameKey];
    // 如果有名字则继续，将蓝牙抛出去
    if (name.length > 0) {
        BIBluetoothModel *bluetoothModel = [BIBluetoothModel new];
        bluetoothModel.peripheral = peripheral;
        bluetoothModel.name = name;
        bluetoothModel.uuid = [peripheral.identifier UUIDString];
        bluetoothModel.manufacturer = [advertisementData[CBAdvertisementDataManufacturerDataKey] reversedByteString];
        
        // 判断是否已经存储过，比如蓝牙设备关闭又打开
        NSInteger repeatIndex = [self yy_checkDeviceModelHasList:self.allPeripherals model:bluetoothModel];
        if (repeatIndex >= 0) {
            [self.allPeripherals replaceObjectAtIndex:repeatIndex withObject:bluetoothModel];
        } else {
            // 存储蓝牙对象
            [self.allPeripherals safeAddObject:bluetoothModel];
        }
        
        // 发送通知
        [self yy_postNotification:kAllBluetoothListNotification object:self.allPeripherals];
        
        NSString *log = [NSString stringWithFormat:@"扫描设备---蓝牙名称=%@;\n蓝牙广播包=%@;\n", bluetoothModel.name, advertisementData];
        BITypeLog(kBluetoothLogType, log);
        
        // 获取之前连接过的蓝牙设备
        NSArray *savedList = [[BIUserDefault sharedInstance] objectForKey:kSaveBluetoothUUIDList];
        NSString *uuid = peripheral.identifier.UUIDString;
        if ([savedList containsObject:uuid]) {
            // 如果已经有连接的设备，则不再自动连接
            if (!self.currentBluetoothModel) {
                [self connectBluetooth:bluetoothModel];
            }
    
            // 判断是否已经存储过，比如蓝牙设备关闭又打开
            NSInteger repeatIndex = [self yy_checkDeviceModelHasList:self.connectedPeripherals model:bluetoothModel];
            if (repeatIndex >= 0) {
                // 在历史列表替换
                [self.connectedPeripherals replaceObjectAtIndex:repeatIndex withObject:bluetoothModel];
            } else {
                // 添加到历史列表
                [self.connectedPeripherals addObject:bluetoothModel];
            }
        }
    }
}

/// 3-1蓝牙连接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    // 连接成功后，停止扫描
    [self stopScan];
    peripheral.delegate = self;
    // 查询蓝牙设备的服务
    [peripheral discoverServices:nil];
    // 发送蓝牙连接成功的结果
    [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (peripheral == obj.peripheral) {
            obj.state = kBIBluetoothConnected;
            NSString *log = [NSString stringWithFormat:@"连接成功---蓝牙名称=%@;\n蓝牙mac地址=%@;\n", obj.name, obj.macAddress];
            BITypeLog(kBluetoothLogType, log);
            [self yy_postNotification:kBluetoothConnectResultNotification object:obj];
            // 每连接成功一个设备，如果历史连接中不存在，则添加
            if (![self.connectedPeripherals containsObject:obj]) {
                [self.connectedPeripherals safeInsertObject:obj index:0];
            }
            *stop = YES;
        }
    }];
    
    NSArray *savedList = [[BIUserDefault sharedInstance] objectForKey:kSaveBluetoothUUIDList];
    // 如果连接成功的设备未存储到本地，则存储，以便下次app重新打开时自动连接
    if (![savedList containsObject:peripheral.identifier.UUIDString]) {
        NSMutableArray *tempArray = [NSMutableArray arrayWithArray:savedList];
        [tempArray safeInsertObject:peripheral.identifier.UUIDString index:0];
        [[BIUserDefault sharedInstance] setObject:tempArray key:kSaveBluetoothUUIDList];
    }
}

/// 3-2蓝牙连接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    // 发送蓝牙连接失败的结果
    [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (peripheral == obj.peripheral) {
            NSString *log = [NSString stringWithFormat:@"连接失败---蓝牙名称=%@;\n蓝牙mac地址=%@;\n", obj.name, obj.macAddress];
            BITypeLog(kBluetoothLogType, log);
            // 清除数据
            [obj disconnectBluetooth];
            [self yy_postNotification:kBluetoothConnectResultNotification object:obj];
            *stop = YES;
        }
    }];
}

/// 4、扫描到的蓝牙设备的服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    [peripheral.services enumerateObjectsUsingBlock:^(CBService * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        // 查询服务对应的特征
        [peripheral discoverCharacteristics:nil forService:obj];
    }];
}

/// 5、扫描到的服务特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    __block BIBluetoothModel *model = nil;
    [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.peripheral == peripheral) {
            model = obj;
            *stop = YES;
        }
    }];
    
    [service.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        // 如果特征属性具有通知属性，则具有读特征
        if (obj.properties == CBCharacteristicPropertyNotify) {
            if ([self.readUUIDDictionary.allKeys containsObject:obj.UUID.UUIDString]) {
                model.readCharacteristic = obj;
                [peripheral discoverDescriptorsForCharacteristic:obj];
                NSString *log = [NSString stringWithFormat:@"读特征匹配---蓝牙名称=%@;\n蓝牙读特征ID=%@;\n", model.name, obj.UUID.UUIDString];
                BITypeLog(kBluetoothLogType, log);
            }
        }
        
        // 如果UUID等于写特征的UUID，则其是写特征
        if ([self.writeUUIDDictionary.allKeys containsObject:obj.UUID.UUIDString]) {
            model.writeCharacteristic = obj;
            NSString *log = [NSString stringWithFormat:@"写特征匹配---蓝牙名称=%@;\n蓝牙写特征ID=%@;\n", model.name, obj.UUID.UUIDString];
            BITypeLog(kBluetoothLogType, log);
            Class cls = NSClassFromString(self.writeUUIDDictionary[obj.UUID.UUIDString]);
            if (cls) {
                model.cmdModel = [cls new];
            }
        }
    }];
}

/// 6、notify特征会进行描述查找
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    
    __block BIBluetoothModel *model = nil;
    [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.peripheral == peripheral) {
            model = obj;
            *stop = YES;
        }
    }];
    
    NSString *key = characteristic.UUID.UUIDString;
    if ([self.readUUIDDictionary.allKeys containsObject:key]) {
        Class cls = NSClassFromString(self.readUUIDDictionary[key]);
        if (cls) {
            model.deviceModel = [cls new];
        }
        // 进行订阅信息，有数据时会触发第7个方法
        [peripheral setNotifyValue:YES forCharacteristic:characteristic];
    }
    [self yy_postNotification:kBluetoothNotifyFinishNotification object:model];
}

/// 7.1、蓝牙外设发送到app的数据
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    // 这个就是硬件发送的数据
    [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.peripheral == peripheral) {
            NSString *log = [NSString stringWithFormat:@"接收数据---蓝牙名称=%@;\n蓝牙数据=%@;\n", obj.name, characteristic.value];
            BITypeLog(kBluetoothLogType, log);
            [obj.deviceModel parserData:characteristic.value];
            [self yy_postNotification:kBluetoothSendDataNotification object:obj];
        }
    }];
}

/// 7.2 给蓝牙设备发送数据的结果
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (error) {
        NSLog(@"%@", error);
    }
}

/// 8、断开蓝牙连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    
    __block BIBluetoothModel *model = nil;
    // 匹配需要移除的模型
    [self.allPeripherals enumerateObjectsUsingBlock:^(BIBluetoothModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (peripheral == obj.peripheral) {
            model = obj;
            *stop = YES;
        }
    }];
    
    // 如果存在，说明匹配到了
    if (model) {
        // 清理数据
        [model disconnectBluetooth];
        // 从列表中移除
        [self.allPeripherals removeObject:model];
        [self.connectedPeripherals removeObject:model];
        // 记录日志
        NSString *log = [NSString stringWithFormat:@"断开设备---蓝牙名称=%@;\n蓝牙mac地址=%@;\n", model.name, model.macAddress];
        BITypeLog(kBluetoothLogType, log);
        // 发送断开通知
        [self yy_postNotification:kBluetoothConnectResultNotification object:model];
        // 发送蓝牙列表通知
        [self yy_postNotification:kAllBluetoothListNotification object:self.allPeripherals];
    }
}

@end
